home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 26 / AACD 26.iso / AACD / Online / x3270 / unix_files / host.c < prev    next >
Encoding:
C/C++ Source or Header  |  2009-02-27  |  17.7 KB  |  815 lines

  1. /*
  2.  * Copyright 1993, 1994, 1995, 1996, 1999, 2000 by Paul Mattes.
  3.  *  Permission to use, copy, modify, and distribute this software and its
  4.  *  documentation for any purpose and without fee is hereby granted,
  5.  *  provided that the above copyright notice appear in all copies and that
  6.  *  both that copyright notice and this permission notice appear in
  7.  *  supporting documentation.
  8.  */
  9.  
  10. /*
  11.  *    host.c
  12.  *        This module handles the ibm_hosts file, connecting to and
  13.  *        disconnecting from hosts, and state changes on the host
  14.  *        connection.
  15.  */
  16.  
  17. #include "globals.h"
  18. #include "appres.h"
  19. #include "resources.h"
  20.  
  21. #include "actionsc.h"
  22. #include "hostc.h"
  23. #include "macrosc.h"
  24. #include "menubarc.h"
  25. #include "popupsc.h"
  26. #include "telnetc.h"
  27. #include "trace_dsc.h"
  28. #include "utilc.h"
  29. #include "xioc.h"
  30.  
  31. #define RECONNECT_MS        2000    /* 2 sec before reconnecting to host */
  32. #define RECONNECT_ERR_MS    5000    /* 5 sec before reconnecting to host */
  33.  
  34. #define MAX_RECENT    5
  35.  
  36. enum cstate    cstate = NOT_CONNECTED;
  37. Boolean        std_ds_host = False;
  38. Boolean        non_tn3270e_host = False;
  39. Boolean        passthru_host = False;
  40. #define        LUNAME_SIZE    16
  41. char        luname[LUNAME_SIZE+1];
  42. char        *connected_lu = CN;
  43. char        *connected_type = CN;
  44. Boolean        ever_3270 = False;
  45.  
  46. char           *current_host = CN;
  47. char           *full_current_host = CN;
  48. unsigned short  current_port;
  49. #if defined(X3270_DISPLAY) /*[*/
  50. char           *reconnect_host = CN;
  51. #endif /*]*/
  52.  
  53. struct host *hosts = (struct host *)NULL;
  54. static struct host *last_host = (struct host *)NULL;
  55. static Boolean auto_reconnect_inprogress = False;
  56. static int net_sock = -1;
  57. static void save_recent(const char *);
  58.  
  59. #if defined(X3270_DISPLAY) /*[*/
  60. static void try_reconnect(void);
  61. #endif /*]*/
  62.  
  63. static char *
  64. stoken(char **s)
  65. {
  66.     char *r;
  67.     char *ss = *s;
  68.  
  69.     if (!*ss)
  70.         return NULL;
  71.     r = ss;
  72.     while (*ss && *ss != ' ' && *ss != '\t')
  73.         ss++;
  74.     if (*ss) {
  75.         *ss++ = '\0';
  76.         while (*ss == ' ' || *ss == '\t')
  77.             ss++;
  78.     }
  79.     *s = ss;
  80.     return r;
  81. }
  82.  
  83.  
  84. /*
  85.  * Read the host file
  86.  */
  87. void
  88. hostfile_init(void)
  89. {
  90.     FILE *hf;
  91.     char buf[1024];
  92.     static Boolean hostfile_initted = False;
  93.     struct host *h;
  94.  
  95.     if (hostfile_initted)
  96.         return;
  97.     else
  98.         hostfile_initted = True;
  99.  
  100.     if (appres.hostsfile == CN)
  101.         appres.hostsfile = xs_buffer("%s/ibm_hosts", LIBX3270DIR);
  102.     hf = fopen(appres.hostsfile, "r");
  103.     if (hf != (FILE *)NULL) {
  104.         while (fgets(buf, sizeof(buf), hf)) {
  105.             char *s = buf;
  106.             char *name, *entry_type, *hostname;
  107.             char *slash;
  108.  
  109.             if (strlen(buf) > (unsigned)1 &&
  110.                 buf[strlen(buf) - 1] == '\n') {
  111.                 buf[strlen(buf) - 1] = '\0';
  112.             }
  113.             while (isspace(*s))
  114.                 s++;
  115.             if (!*s || *s == '#')
  116.                 continue;
  117.             name = stoken(&s);
  118.             entry_type = stoken(&s);
  119.             hostname = stoken(&s);
  120.             if (!name || !entry_type || !hostname) {
  121.                 xs_warning("Bad %s syntax, entry skipped",
  122.                     ResHostsFile);
  123.                 continue;
  124.             }
  125.             h = (struct host *)Malloc(sizeof(*h));
  126.             h->name = NewString(name);
  127.             h->hostname = NewString(hostname);
  128.  
  129.             /*
  130.              * Quick syntax extension to allow the hosts file to
  131.              * specify a port as host/port.
  132.              */
  133.             if ((slash = strchr(h->hostname, '/')))
  134.                 *slash = ':';
  135.  
  136.             if (!strcmp(entry_type, "primary"))
  137.                 h->entry_type = PRIMARY;
  138.             else
  139.                 h->entry_type = ALIAS;
  140.             if (*s)
  141.                 h->loginstring = NewString(s);
  142.             else
  143.                 h->loginstring = CN;
  144.             h->prev = last_host;
  145.             h->next = (struct host *)NULL;
  146.             if (last_host)
  147.                 last_host->next = h;
  148.             else
  149.                 hosts = h;
  150.             last_host = h;
  151.         }
  152.         (void) fclose(hf);
  153.     }
  154.  
  155.     /*
  156.      * Read the recent-connection file, and prepend it to the hosts list.
  157.      */
  158.     save_recent(CN);
  159. }
  160.  
  161. /*
  162.  * Look up a host in the list.  Turns aliases into real hostnames, and
  163.  * finds loginstrings.
  164.  */
  165. static int
  166. hostfile_lookup(const char *name, char **hostname, char **loginstring)
  167. {
  168.     struct host *h;
  169.  
  170.     hostfile_init();
  171.     for (h = hosts; h != (struct host *)NULL; h = h->next) {
  172.         if (h->entry_type == RECENT)
  173.             continue;
  174.         if (!strcmp(name, h->name)) {
  175.             *hostname = h->hostname;
  176.             *loginstring = h->loginstring;
  177.             return 1;
  178.         }
  179.     }
  180.     return 0;
  181. }
  182.  
  183. #if defined(LOCAL_PROCESS) /*[*/
  184. /* Recognize and translate "-e" options. */
  185. static const char *
  186. parse_localprocess(const char *s)
  187. {
  188.     int sl = strlen(OptLocalProcess);
  189.  
  190.     if (!strncmp(s, OptLocalProcess, sl)) {
  191.         if (s[sl] == ' ')
  192.             return(s + sl + 1);
  193.         else if (s[sl] == '\0') {
  194.             char *r;
  195.  
  196.             r = getenv("SHELL");
  197.             if (r != CN)
  198.                 return r;
  199.             else
  200.                 return "/bin/sh";
  201.         }
  202.     }
  203.     return CN;
  204. }
  205. #endif /*]*/
  206.  
  207. /*
  208.  * Strip qualifiers from a hostname.
  209.  * Returns the hostname part in a newly-malloc'd string.
  210.  */
  211. static char *
  212. split_host(char *s, Boolean *ansi, Boolean *std_ds, Boolean *passthru,
  213.     Boolean *non_e, char *xluname, char **port)
  214. {
  215.     *ansi = False;
  216.     *std_ds = False;
  217.     *passthru = False;
  218.     *non_e = False;
  219.     *xluname = '\0';
  220.     *port = CN;
  221.  
  222.     for (;;) {
  223.         char *at;
  224.  
  225.         if (!strncmp(s, "a:", 2) || !strncmp(s, "A:", 2)) {
  226.             *ansi = True;
  227.             s += 2;
  228.             continue;
  229.         }
  230.         if (!strncmp(s, "s:", 2) || !strncmp(s, "S:", 2)) {
  231.             *std_ds = True;
  232.             s += 2;
  233.             continue;
  234.         }
  235.         if (!strncmp(s, "p:", 2) || !strncmp(s, "P:", 2)) {
  236.             *passthru = True;
  237.             s += 2;
  238.             continue;
  239.         }
  240.         if (!strncmp(s, "n:", 2) || !strncmp(s, "N:", 2)) {
  241.             *non_e = True;
  242.             s += 2;
  243.             continue;
  244.         }
  245.         if ((at = strchr(s, '@')) != NULL) {
  246.             if (at != s) {
  247.                 int nc = at - s;
  248.         
  249.                 if (nc > LUNAME_SIZE)
  250.                     nc = LUNAME_SIZE;
  251.                 (void) strncpy(xluname, s, nc);
  252.                 xluname[nc] = '\0';
  253.             }
  254.             s = at + 1;
  255.             continue;
  256.         }
  257.  
  258.         break;
  259.     }
  260.     if (*s) {
  261.         char *r;
  262.         char *sep;
  263.  
  264.         r = NewString(s);
  265.         sep = strrchr(r, ':');
  266.         if (sep == NULL)
  267.             sep = strrchr(r, ' ');
  268.         else if (strrchr(r, ' ') != NULL) {
  269.             Free(r);
  270.             return CN;
  271.         }
  272.         if (sep != CN) {
  273.             *sep++ = '\0';
  274.             while (*sep == ' ')
  275.                 sep++;
  276.         }
  277.         if (port != (char **) NULL)
  278.             *port = sep;
  279.         return r;
  280.     } else
  281.         return CN;
  282. }
  283.  
  284.  
  285. /*
  286.  * Network connect/disconnect operations, combined with X input operations.
  287.  *
  288.  * Returns 0 for success, -1 for error.
  289.  * Sets 'reconnect_host', 'current_host' and 'full_current_host' as
  290.  * side-effects.
  291.  */
  292. int
  293. host_connect(const char *n)
  294. {
  295.     char nb[2048];        /* name buffer */
  296.     char *s;        /* temporary */
  297.     const char *chost;    /* to whom we will connect */
  298.     char *target_name;
  299.     char *ps = CN;
  300.     char *port = CN;
  301.     Boolean pending;
  302.     static Boolean ansi_host;
  303.     const char *localprocess_cmd = NULL;
  304.  
  305.     if (CONNECTED || auto_reconnect_inprogress)
  306.         return 0;
  307.  
  308.     /* Skip leading blanks. */
  309.     while (*n == ' ')
  310.         n++;
  311.     if (!*n) {
  312.         popup_an_error("Invalid (empty) hostname");
  313.         return -1;
  314.     }
  315.  
  316.     /* Save in a modifiable buffer. */
  317.     (void) strcpy(nb, n);
  318.  
  319.     /* Strip trailing blanks. */
  320.     s = nb + strlen(nb) - 1;
  321.     while (*s == ' ')
  322.         *s-- = '\0';
  323.  
  324. #if defined(X3270_DISPLAY) /*[*/
  325.     /* Remember this hostname, as the last hostname we connected to. */
  326.     if (reconnect_host != CN)
  327.         Free(reconnect_host);
  328.     reconnect_host = NewString(nb);
  329. #endif /*]*/
  330.  
  331.     /* Remember this hostname in the recent connection list and file. */
  332.     save_recent(nb);
  333.  
  334. #if defined(LOCAL_PROCESS) /*[*/
  335.     if ((localprocess_cmd = parse_localprocess(nb)) != CN) {
  336.         chost = localprocess_cmd;
  337.         port = appres.port;
  338.     } else
  339. #endif /*]*/
  340.     {
  341.         /* Strip off and remember leading qualifiers. */
  342.         if ((s = split_host(nb, &ansi_host, &std_ds_host,
  343.             &passthru_host, &non_tn3270e_host, luname, &port)) == CN)
  344.             return -1;
  345.  
  346.         /* Look up the name in the hosts file. */
  347.         if (hostfile_lookup(s, &target_name, &ps)) {
  348.             /*
  349.              * Rescan for qualifiers.
  350.              * Qualifiers, LU names, and ports are all overridden
  351.              * by the hosts file.
  352.              */
  353.             Free(s);
  354.             if (!(s = split_host(target_name, &ansi_host,
  355.                 &std_ds_host, &passthru_host, &non_tn3270e_host,
  356.                 luname, &port)))
  357.                 return -1;
  358.         }
  359.         chost = s;
  360.  
  361.         /* Default the port. */
  362.         if (port == CN)
  363.             port = appres.port;
  364.     }
  365.  
  366.     /*
  367.      * Store the original name in globals, even if we fail the connect
  368.      * later:
  369.      *  current_host is the hostname part, stripped of qualifiers, luname
  370.      *   and port number
  371.      *  full_current_host is the entire string, for use in reconnecting
  372.      */
  373.     if (n != full_current_host) {
  374.         if (full_current_host != CN)
  375.             Free(full_current_host);
  376.         full_current_host = NewString(n);
  377.     }
  378.     if (current_host != CN)
  379.         Free(current_host);
  380.     if (localprocess_cmd != CN) {
  381.         if (full_current_host[strlen(OptLocalProcess)] != '\0')
  382.         current_host = NewString(full_current_host +
  383.             strlen(OptLocalProcess) + 1);
  384.         else
  385.             current_host = NewString("default shell");
  386.     } else {
  387.         current_host = s;
  388.     }
  389.  
  390.     /* Attempt contact. */
  391.     ever_3270 = False;
  392.     net_sock = net_connect(chost, port, localprocess_cmd != CN, &pending);
  393.     if (net_sock < 0) {
  394. #if defined(X3270_DISPLAY) /*[*/
  395.         if (appres.once) {
  396.             /* Exit when the error pop-up pops down. */
  397.             exiting = True;
  398.         }
  399.         else if (appres.reconnect) {
  400.             auto_reconnect_inprogress = True;
  401.             (void) AddTimeOut(RECONNECT_ERR_MS, try_reconnect);
  402.         }
  403. #endif /*]*/
  404.         /* Redundantly signal a disconnect. */
  405.         st_changed(ST_CONNECT, False);
  406.         return -1;
  407.     }
  408.  
  409.     /* Success. */
  410.  
  411.     /* Set pending string. */
  412.     if (ps != CN)
  413.         login_macro(ps);
  414.  
  415.     /* Prepare Xt for I/O. */
  416.     x_add_input(net_sock);
  417.  
  418.     /* Set state and tell the world. */
  419.     if (pending) {
  420.         cstate = PENDING;
  421.         st_changed(ST_HALF_CONNECT, True);
  422.     } else {
  423.         cstate = CONNECTED_INITIAL;
  424.         st_changed(ST_CONNECT, True);
  425. #if defined(X3270_DISPLAY) /*[*/
  426.         if (appres.reconnect && error_popup_visible())
  427.             popdown_an_error();
  428. #endif /*]*/
  429.     }
  430.  
  431.     return 0;
  432. }
  433.  
  434. #if defined(X3270_DISPLAY) /*[*/
  435. /*
  436.  * Reconnect to the last host.
  437.  */
  438. static void
  439. host_reconnect(void)
  440. {
  441.     if (auto_reconnect_inprogress || current_host == CN ||
  442.         CONNECTED || HALF_CONNECTED)
  443.         return;
  444.     if (host_connect(reconnect_host) >= 0)
  445.         auto_reconnect_inprogress = False;
  446. }
  447.  
  448. /*
  449.  * Called from timer to attempt an automatic reconnection.
  450.  */
  451. static void
  452. try_reconnect(void)
  453. {
  454.     auto_reconnect_inprogress = False;
  455.     host_reconnect();
  456. }
  457. #endif /*]*/
  458.  
  459. void
  460. host_disconnect(Boolean failed)
  461. {
  462.     if (CONNECTED || HALF_CONNECTED) {
  463.         x_remove_input();
  464.         net_disconnect();
  465.         net_sock = -1;
  466. #if defined(X3270_DISPLAY) /*[*/
  467.         if (appres.once) {
  468.             if (error_popup_visible()) {
  469.                 /*
  470.                  * If there is an error pop-up, exit when it
  471.                  * pops down.
  472.                  */
  473.                 exiting = True;
  474.             } else {
  475.                 /* Exit now. */
  476.                 x3270_exit(0);
  477.                 return;
  478.             }
  479.         } else if (appres.reconnect && !auto_reconnect_inprogress) {
  480.             /* Schedule an automatic reconnection. */
  481.             auto_reconnect_inprogress = True;
  482.             (void) AddTimeOut(failed? RECONNECT_ERR_MS:
  483.                            RECONNECT_MS,
  484.                       try_reconnect);
  485.         }
  486. #endif /*]*/
  487.  
  488.         /*
  489.          * Remember a disconnect from ANSI mode, to keep screen tracing
  490.          * in sync.
  491.          */
  492. #if defined(X3270_TRACE) /*[*/
  493.         if (IN_ANSI && toggled(SCREEN_TRACE))
  494.             trace_ansi_disc();
  495. #endif /*]*/
  496.  
  497.         cstate = NOT_CONNECTED;
  498.  
  499.         /* Propagate the news to everyone else. */
  500.         st_changed(ST_CONNECT, False);
  501.     }
  502. }
  503.  
  504. /* The host has entered 3270 or ANSI mode, or switched between them. */
  505. void
  506. host_in3270(enum cstate new_cstate)
  507. {
  508.     Boolean now3270 = (new_cstate == CONNECTED_3270 ||
  509.                new_cstate == CONNECTED_SSCP ||
  510.                new_cstate == CONNECTED_TN3270E);
  511.  
  512.     cstate = new_cstate;
  513.     ever_3270 = now3270;
  514.     st_changed(ST_3270_MODE, now3270);
  515. }
  516.  
  517. void
  518. host_connected(void)
  519. {
  520.     cstate = CONNECTED_INITIAL;
  521.     st_changed(ST_CONNECT, True);
  522.  
  523. #if defined(X3270_DISPLAY) /*[*/
  524.     if (appres.reconnect && error_popup_visible())
  525.         popdown_an_error();
  526. #endif /*]*/
  527. }
  528.  
  529. /* Comparison function for the qsort. */
  530. static int
  531. host_compare(const void *e1, const void *e2)
  532. {
  533.     const struct host *h1 = *(const struct host **)e1;
  534.     const struct host *h2 = *(const struct host **)e2;
  535.     int r;
  536.  
  537.     if (h1->connect_time > h2->connect_time)
  538.         r = -1;
  539.     else if (h1->connect_time < h2->connect_time)
  540.         r = 1;
  541.     else
  542.         r = 0;
  543. #if defined(CFDEBUG) /*[*/
  544.     printf("%s %ld %d %s %ld\n",
  545.         h1->name, h1->connect_time,
  546.         r,
  547.         h2->name, h2->connect_time);
  548. #endif /*]*/
  549.     return r;
  550. }
  551.  
  552. #if defined(CFDEBUG) /*[*/
  553. static void
  554. dump_array(const char *when, struct host **array, int nh)
  555. {
  556.     int i;
  557.  
  558.     printf("%s\n", when);
  559.     for (i = 0; i < nh; i++) {
  560.         printf(" %15s %ld\n", array[i]->name, array[i]->connect_time);
  561.     }
  562. }
  563. #endif /*]*/
  564.  
  565. /* Save the most recent host in the recent host list. */
  566. static void
  567. save_recent(const char *hn)
  568. {
  569.     char *lcf_name = CN;
  570.     FILE *lcf = (FILE *)NULL;
  571.     struct host *h;
  572.     struct host *rest = (struct host *)NULL;
  573.     int n_ent = 0;
  574.     struct host *h_array[(MAX_RECENT * 2) + 1];
  575.     int nh = 0;
  576.     int i, j;
  577.     time_t t = time((time_t *)NULL);
  578.  
  579.     /* Allocate a new entry. */
  580.     if (hn != CN) {
  581.         h = (struct host *)Malloc(sizeof(*h));
  582.         h->name = NewString(hn);
  583.         h->hostname = NewString(hn);
  584.         h->entry_type = RECENT;
  585.         h->loginstring = CN;
  586.         h->connect_time = t;
  587.         h_array[nh++] = h;
  588.     }
  589.  
  590.     /* Put the existing entries into the array. */
  591.     for (h = hosts; h != (struct host *)NULL; h = h->next) {
  592.         if (h->entry_type != RECENT)
  593.             break;
  594.         h_array[nh++] = h;
  595.     }
  596.  
  597.     /* Save the ibm_hosts entries for later. */
  598.     rest = h;
  599.     if (rest != (struct host *)NULL)
  600.         rest->prev = (struct host *)NULL;
  601.  
  602.     /*
  603.      * Read the last-connection file, to capture the any changes made by
  604.      * other instances of x3270.  
  605.      */
  606.     if (appres.connectfile_name != CN &&
  607.         strcasecmp(appres.connectfile_name, "none")) {
  608.         lcf_name = do_subst(appres.connectfile_name, True, True);
  609.         lcf = fopen(lcf_name, "r");
  610.     }
  611.     if (lcf != (FILE *)NULL) {
  612.         char buf[1024];
  613.  
  614.         while (fgets(buf, sizeof(buf), lcf) != CN) {
  615.             int sl;
  616.             time_t connect_time;
  617.             char *ptr;
  618.  
  619.             /* Pick apart the entry. */
  620.             sl = strlen(buf);
  621.             if (buf[sl - 1] == '\n')
  622.                 buf[sl-- - 1] = '\0';
  623.             if (!sl ||
  624.                 buf[0] == '#' ||
  625.                 (connect_time = strtoul(buf, &ptr, 10)) == 0L ||
  626.                 ptr == buf ||
  627.                 *ptr != ' ' ||
  628.                 !*(ptr + 1))
  629.                 continue;
  630.  
  631.             h = (struct host *)Malloc(sizeof(*h));
  632.             h->name = NewString(ptr + 1);
  633.             h->hostname = NewString(ptr + 1);
  634.             h->entry_type = RECENT;
  635.             h->loginstring = CN;
  636.             h->connect_time = connect_time;
  637.             h_array[nh++] = h;
  638.             if (nh > (MAX_RECENT * 2) + 1)
  639.                 break;
  640.         }
  641.         fclose(lcf);
  642.     }
  643.  
  644.     /* Sort the array, in reverse order by connect time. */
  645. #if defined(CFDEBUG) /*[*/
  646.     dump_array("before", h_array, nh);
  647. #endif /*]*/
  648.     qsort(h_array, nh, sizeof(struct host *), host_compare);
  649. #if defined(CFDEBUG) /*[*/
  650.     dump_array("after", h_array, nh);
  651. #endif /*]*/
  652.  
  653.     /*
  654.      * Filter out duplicate host names, and limit the array to
  655.      * MAX_RECENT entries total.
  656.      */
  657.     hosts = (struct host *)NULL;
  658.     last_host = (struct host *)NULL;
  659.     for (i = 0; i < nh; i++) {
  660.         h = h_array[i];
  661.         if (h == (struct host *)NULL)
  662.             continue;
  663.         h->next = (struct host *)NULL;
  664.         if (last_host != (struct host *)NULL)
  665.             last_host->next = h;
  666.         h->prev = last_host;
  667.         last_host = h;
  668.         if (hosts == (struct host *)NULL)
  669.             hosts = h;
  670.         n_ent++;
  671.  
  672.         /* Zap the duplicates. */
  673.         for (j = i+1; j < nh; j++) {
  674.             if (h_array[j] &&
  675.                 (n_ent >= MAX_RECENT ||
  676.                  !strcmp(h_array[i]->name, h_array[j]->name))) {
  677. #if defined(CFDEBUG) /*[*/
  678.                 printf("%s is a dup of %s\n",
  679.                     h_array[j]->name, h_array[i]->name);
  680. #endif /*]*/
  681.                 Free(h_array[j]->name);
  682.                 Free(h_array[j]->hostname);
  683.                 Free(h_array[j]);
  684.                 h_array[j] = (struct host *)NULL;
  685.             }
  686.         }
  687.     }
  688.  
  689.     /* Re-attach the ibm_hosts entries to the end. */
  690.     if (rest != (struct host *)NULL) {
  691.         if (last_host != (struct host *)NULL) {
  692.             last_host->next = rest;
  693.         } else {
  694.             hosts = rest;
  695.         }
  696.         rest->prev = last_host;
  697.     }
  698.  
  699.     /* If there's been a change, rewrite the file. */
  700.     if (hn != CN &&
  701.         appres.connectfile_name != CN &&
  702.         strcasecmp(appres.connectfile_name, "none")) {
  703.         lcf = fopen(lcf_name, "w");
  704.         if (lcf != (FILE *)NULL) {
  705.             fprintf(lcf, "# Created %s# by %s\n", ctime(&t), build);
  706.             for (h = hosts; h != (struct host *)NULL; h = h->next) {
  707.                 if (h->entry_type != RECENT)
  708.                     break;
  709.                 (void) fprintf(lcf, "%lu %s\n", h->connect_time,
  710.                     h->name);
  711.             }
  712.         }
  713.         fclose(lcf);
  714.         Free(lcf_name);
  715.     }
  716. }
  717.  
  718. /* Support for state change callbacks. */
  719.  
  720. struct st_callback {
  721.     struct st_callback *next;
  722.     void (*func)(Boolean);
  723. };
  724. static struct st_callback *st_callbacks[N_ST];
  725. static struct st_callback *st_last[N_ST];
  726.  
  727. /* Register a function interested in a state change. */
  728. void
  729. register_schange(int tx, void (*func)(Boolean))
  730. {
  731.     struct st_callback *st;
  732.  
  733.     st = (struct st_callback *)Malloc(sizeof(*st));
  734.     st->func = func;
  735.     st->next = (struct st_callback *)NULL;
  736.     if (st_last[tx] != (struct st_callback *)NULL)
  737.         st_last[tx]->next = st;
  738.     else
  739.         st_callbacks[tx] = st;
  740.     st_last[tx] = st;
  741. }
  742.  
  743. /* Signal a state change. */
  744. void
  745. st_changed(int tx, Boolean mode)
  746. {
  747.     struct st_callback *st;
  748.  
  749.     for (st = st_callbacks[tx];
  750.          st != (struct st_callback *)NULL;
  751.          st = st->next) {
  752.         (*st->func)(mode);
  753.     }
  754. }
  755.  
  756. /* Explicit connect/disconnect actions. */
  757.  
  758. void
  759. Connect_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
  760. {
  761.     action_debug(Connect_action, event, params, num_params);
  762.     if (check_usage(Connect_action, *num_params, 1, 1) < 0)
  763.         return;
  764.     if (CONNECTED || HALF_CONNECTED) {
  765.         popup_an_error("Already connected");
  766.         return;
  767.     }
  768.     (void) host_connect(params[0]);
  769.  
  770.     /*
  771.      * If called from a script and the connection was successful (or
  772.      * half-successful), pause the script until we are connected and
  773.      * we have identified the host type.
  774.      */
  775.     if (!w && (CONNECTED || HALF_CONNECTED))
  776.         sms_connect_wait();
  777. }
  778.  
  779. #if defined(X3270_MENUS) /*[*/
  780. void
  781. Reconnect_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
  782. {
  783.     action_debug(Reconnect_action, event, params, num_params);
  784.     if (check_usage(Reconnect_action, *num_params, 0, 0) < 0)
  785.         return;
  786.     if (CONNECTED || HALF_CONNECTED) {
  787.         popup_an_error("Already connected");
  788.         return;
  789.     }
  790.     if (current_host == CN) {
  791.         popup_an_error("No previous host to connect to");
  792.         return;
  793.     }
  794.     host_reconnect();
  795.  
  796.     /*
  797.      * If called from a script and the connection was successful (or
  798.      * half-successful), pause the script until we are connected and
  799.      * we have identified the host type.
  800.      */
  801.     if (!w && (CONNECTED || HALF_CONNECTED))
  802.         sms_connect_wait();
  803. }
  804. #endif /*]*/
  805.  
  806. void
  807. Disconnect_action(Widget w unused, XEvent *event, String *params,
  808.     Cardinal *num_params)
  809. {
  810.     action_debug(Disconnect_action, event, params, num_params);
  811.     if (check_usage(Disconnect_action, *num_params, 0, 0) < 0)
  812.         return;
  813.     host_disconnect(False);
  814. }
  815.